Skip to content

Fix #5946: $this type of static (self, ...) type must remains static#4902

Merged
ondrejmirtes merged 1 commit into2.1.xfrom
create-pull-request/patch-molh6lo
Feb 12, 2026
Merged

Fix #5946: $this type of static (self, ...) type must remains static#4902
ondrejmirtes merged 1 commit into2.1.xfrom
create-pull-request/patch-molh6lo

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When a method with @return $this is called on a static return type, the result type was incorrectly preserved as $this instead of being downgraded to static. This caused PHPStan to miss reporting a type error when returning the result of such a chain from a method that requires $this.

For example:

class Model {
    /** @return static */
    public function getParent() { return new static(); }

    /** @return $this */
    public function getModel(bool $useParent) {
        if ($useParent) {
            return $this->getParent()->getModel(false); // should error but didn't
        }
        return $this;
    }
}

$this->getParent() returns static(Model), then calling getModel() on that should yield static(Model) (not $this(Model)), because $this cannot be guaranteed when the caller is merely static.

Changes

  • src/Type/StaticType.php: In transformStaticType(), after changeBaseClass(), check if the resulting type is ThisType and the caller ($this) is not a ThisType — if so, construct a plain StaticType instead
  • tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php: Added testBug5946() test method
  • tests/PHPStan/Rules/Methods/data/bug-5946.php: New test data file reproducing the issue
  • CLAUDE.md: Added documentation about StaticType::transformStaticType and ThisType downgrading

Root cause

StaticType::transformStaticType() uses $type->changeBaseClass($classReflection) to transform return types. Since ThisType extends StaticType, both are caught by the instanceof StaticType check. However, ThisType::changeBaseClass() returns a new ThisType (preserving $this semantics), when it should have been downgraded to a plain StaticType for callers that are static but not $this. The fix explicitly checks for this case and constructs a StaticType instead.

Test

Added a regression test with a Model class that has a getParent() returning static and a getModel() returning $this. The test verifies that both $this->getParent() and $this->getParent()->getModel(false) are correctly reported as returning static(Model) instead of $this(Model).

Fixes phpstan/phpstan#5946

- In StaticType::transformStaticType(), when the caller is a StaticType
  (not ThisType) and the method return type is ThisType, downgrade it
  to StaticType since $this cannot be guaranteed on a static caller
- New regression test in tests/PHPStan/Rules/Methods/data/bug-5946.php
- The root cause was that ThisType::changeBaseClass() returns a new
  ThisType, preserving $this semantics even when the caller is only static

Fixes phpstan/phpstan#5946
@ondrejmirtes ondrejmirtes merged commit e30d1e5 into 2.1.x Feb 12, 2026
628 of 641 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-molh6lo branch February 12, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

$this type of static (self, ...) type must remains static

2 participants